package org.session.repository.redis;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;

import org.session.model.AddEntryToSessionRequest;
import org.session.model.CreateSessionRequest;
import org.session.model.DeleteKeyFromSessionRequest;
import org.session.model.DeleteSessionRequest;
import org.session.model.EndSessionRequest;
import org.session.model.GetEntriesFromSessionRequest;
import org.session.model.GetEntryFromSessionRequest;
import org.session.model.ReplaceEntryInSessionRequest;
import org.session.model.RepositoryType;
import org.session.model.SearchSessionRequest;
import org.session.model.Session;
import org.session.model.SessionEntry;
import org.session.model.SessionImpl;
import org.session.service.RequestThrottlerInterceptor;
import org.session.util.CharacterTools;
import org.session.util.SerializationTools;

import redis.clients.jedis.Jedis;

/**
 * TODO: implement me
 */
public final class RedisProxyAdapter extends RequestThrottlerInterceptor {
    private static final Logger logger = Logger
            .getLogger("org.session.repository.redis.RedisProxyAdapter");
    private static final RedisConnectionManager connectionManager = RedisConnectionManager
            .getInstance();

    @Override
    public RepositoryType getRepositoryType() {
        return RepositoryType.REDIS;
    }

    @Override
    public Future<Boolean> pingRepository() {
        return executor.submit(new Callable<Boolean>() {
            public Boolean call() throws Exception {
                Jedis jedis = (Jedis) connectionManager.getConnection();
                try {
                    // ping 4 times 250msec apart
                    boolean one = jedis.isConnected();
                    Thread.sleep(250L);
                    boolean two = jedis.isConnected();
                    Thread.sleep(250L);
                    boolean three = jedis.isConnected();
                    Thread.sleep(250L);
                    boolean four = jedis.isConnected();

                    return (one && two && three && four);
                } finally {
                    connectionManager.closeConnection(jedis);
                }
            }
        });
    }

    @Override
    public Future<Boolean> shutdownRepository() {
        return executor.submit(new Callable<Boolean>() {
            public Boolean call() throws Exception {
                return connectionManager.shutdown();
            }
        });
    }

    @Override
    protected Future<Session> createSession(final CreateSessionRequest request) {
        return executor.submit(new Callable<Session>() {
            public Session call() throws Exception {
                Jedis jedis = (Jedis) connectionManager.getConnection();
                Session session = new SessionImpl(getRepositoryType(),
                        getRepositoryType());
                try {
                    String sessionId = session.getSessionId();
                    Map<String, String> sessionMap = new HashMap<String, String>();
                    String sessionData = SerializationTools
                            .serializeObjectToString(session);
                    sessionMap.put(sessionId, sessionData);
                    String status = jedis.hmset(sessionId, sessionMap);
                    assert ("OK".equals(status));
                } finally {
                    connectionManager.closeConnection(jedis);
                }

                return session;
            }
        });
    }

    @Override
    protected Future<Boolean> endSession(final EndSessionRequest request) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected Future<Boolean> deleteSession(final DeleteSessionRequest request) {
        return executor.submit(new Callable<Boolean>() {
            public Boolean call() throws Exception {
                Jedis jedis = (Jedis) connectionManager.getConnection();
                Boolean success = false;
                try {
                    String sessionId = request.getSessionId();
                    if (!CharacterTools.isNullOrEmpty(sessionId)) {
                        jedis.del(sessionId);
                        success = true;
                    }
                } finally {
                    connectionManager.closeConnection(jedis);
                }

                return success;
            }
        });
    }

    @Override
    protected Future<Session> searchSession(final SearchSessionRequest request) {
        return executor.submit(new Callable<Session>() {
            public Session call() throws Exception {
                Jedis jedis = (Jedis) connectionManager.getConnection();
                Session session = null;
                try {
                    String sessionId = request.getSessionId();
                    if (!CharacterTools.isNullOrEmpty(sessionId)) {
                        Map<String, String> sessionMap = jedis
                                .hgetAll(sessionId);
                        session = (Session) SerializationTools
                                .deserializeObjectFromString(sessionMap
                                        .get(sessionId));
                    }
                } finally {
                    connectionManager.closeConnection(jedis);
                }

                return session;
            }
        });
    }

    @Override
    protected Future<Boolean> addEntryToSession(
            final AddEntryToSessionRequest request) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected Future<Boolean> replaceEntryInSession(
            final ReplaceEntryInSessionRequest request) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected Future<SessionEntry> getEntryFromSession(
            final GetEntryFromSessionRequest request) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected Future<Map<String, SessionEntry>> getEntriesFromSession(
            final GetEntriesFromSessionRequest request) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected Future<Boolean> deleteKeyFromSession(
            final DeleteKeyFromSessionRequest request) {
        throw new UnsupportedOperationException();
    }

}
